#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<errno.h>
#include<sys/wait.h>
#include<ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SIZE 512
#define NUM 32
#define ZERO '\0'
#define SEP " "//这里必须是字符串

#define SkipPath(p) do{ p+=(strlen(p)-1);while(*p!='/')p--;}while(0)

#define SkipSpace(cmd,pos) do{\
while(1){\
  if(isspace(cmd[pos]))\
    pos++;\
  else \
    break;\
  }\
}while(0)

#define NON_REDIR 0
#define IN_REDIR 1
#define OUT_REDIR 2
#define APP_REDIR 3

char* filename=NULL;
char cwd[SIZE*2];
char* Argv[NUM];
int lastcode=0;
int redir_type=0;

char* GetUserName()
{
  char * name=getenv("USER");
  if(name==NULL)
    return (char*)"None";
  return name;
}
char*GetHostName()
{
  char* hostname=getenv("HOSTNAME");
  if(hostname==NULL)
    return (char*)"None";
  return hostname;
}
char * GetCwd()
{
  char* pwd=getenv("PWD");
  if(pwd==NULL)
    return (char*)"None";
  return pwd;
}
void MakeCommandLineAndPrint()
{
  char line[SIZE];
  char* username=GetUserName();
  char *hostname=GetHostName();
  char *cwd=GetCwd();
 
  SkipPath(cwd);
  snprintf(line,sizeof(line),"[%s@%s %s]$",username,hostname,cwd+1);
  printf("%s",line);
  fflush(stdout);
    
}

int GetCommand(char usercommand[],int size)
{
  char *s=fgets(usercommand,size,stdin);
  if(s==NULL)return -1;
  //结尾是 '\n''0'这里会多一个换行，我们需要帮前一个'\n'变为'0'
  usercommand[strlen(usercommand)-1]=ZERO;
  return strlen(usercommand);
}

void SplitCommand(char usercommand[],size_t n)
{
  Argv[0]=strtok(usercommand,SEP);
  int i=1;
  while((Argv[i++]=strtok(NULL,SEP)));//多个括号消除警告
}

void ExcuteCommand()
{
  //创建子进程执行命令
  int status=0;
  pid_t id=fork();
  if(id<0) exit(1);
  if(id==0)
  {
    if(filename!=NULL)
    {
      if(redir_type==IN_REDIR)
      {
        int fd=open(filename,O_RDONLY);
        dup2(fd,0);
      }
      else if(redir_type==APP_REDIR)
      {
        int fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
        dup2(fd,1);
      }
      else 
      {
        int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
        dup2(fd,1);
      }
    }
    //child
    execvp(Argv[0],Argv);
    exit(errno);
  }
  else 
  {
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
      lastcode=WEXITSTATUS(status);
      if(lastcode!=0)
        printf("%s:%s:%d\n",Argv[0],strerror(lastcode),lastcode);
    }
  }

}

char *GetHome()
{
  char* home=getenv("HOME");
  if(home==NULL) return "/";
  return home;
}
void CD()
{
  char *path=Argv[1];
  if(path==NULL)path=GetHome();

  //path一定存在，cd不跟路径的时候默认进入家目录
  chdir(path);
  
  //刷新环境变量
  char tmp[SIZE];
  getcwd(tmp,sizeof(tmp));
  snprintf(cwd,sizeof(cwd),"PWD=%s",tmp);
  putenv(cwd);
}


int CheckBuildin(char usercommand[],int n)
{
  int yes=0;
  if(strcmp(Argv[0],"cd")==0)
  {
    yes=1;
    CD();
  }
  else if(strcmp(Argv[0],"echo")==0)
  {
    yes=1;
    printf("%d\n",lastcode);
    lastcode=0;
  }
  
  return yes;

}

void CheckRedir(char usercommand[])
{
  //检查是否有 重定向符号
  int pos=0;
  int end=strlen(usercommand);

  while(pos<end)
  {
    if(usercommand[pos]=='<')
    {
      usercommand[pos++]=0;
      redir_type=IN_REDIR;
      SkipSpace(usercommand,pos);
      filename=usercommand+pos;
    }
    if(usercommand[pos]=='>')
    {
      if(usercommand[pos+1]=='>')
      {
        usercommand[pos++]=0;
        pos++;
        SkipSpace(usercommand,pos);
        redir_type=APP_REDIR;
        filename=usercommand+pos;
      }
      else 
      {
        usercommand[pos++]=0;
        SkipSpace(usercommand,pos);
        redir_type=OUT_REDIR;
        filename=usercommand+pos;
      }
    }
    pos++;
  }
}

int main()
{
  int quit=0;
  while(!quit)
  {
    //制作命令行并且打印命令行
    MakeCommandLineAndPrint();
    
    //获取用户命令字符串
    char usercommand[SIZE];
    int n=GetCommand(usercommand,SIZE);
    if(n<=0)
      return -1;
    //checkredir
    filename=NULL;
    CheckRedir(usercommand);
    
    //分割字符串
    SplitCommand(usercommand,sizeof(usercommand));
   
    //检查是否是内建命令
    n=CheckBuildin(usercommand,sizeof(usercommand));
    if(n!=0) continue;

    //执行命令
    ExcuteCommand();
  }




  return 0;
}
